<?php

namespace App\Models\Trip\TripOrderRefund\Refunds;

use App\Models\Train\TrainOrderPassengers\TrainOrderPassenger;
use App\Models\Train\TrainOrders\TrainOrder;
use App\Models\Trip\TripOrderCosts\Repositories\Interfaces\TripOrderCostsRepositoryInterface;
use App\Models\Trip\TripOrderCosts\Repositories\Interfaces\TripOrderPassengerCostsRepositoryInterface;
use App\Models\Trip\TripOrderCosts\TripOrderCost;
use App\Models\Trip\TripOrderCosts\TripOrderPassengerCost;
use App\Models\Trip\TripOrderRefund\RefundDetail;
use App\Models\Trip\TripOrderRefund\TripOrderPassengerRefundDetail;
use App\Models\Trip\TripOrderRefund\TripOrderRefund;
use App\Trip\Enums\OrderBusiness;
use App\Trip\Enums\TripTypes;
use Illuminate\Support\Facades\Log;
use JetBrains\PhpStorm\ArrayShape;

class dealTicketPriceDiffRefund implements dealRefundInterface
{
    private TrainOrder $order;
    private string     $log_key;
    private array      $refundInfos;
    private array      $refund_datas;
    /**
     * @var false
     */
    private bool $has_alone_pay_record;

    public function __construct(TrainOrder $order_info)
    {
        $this->order                = $order_info;
        $this->log_key              = "订单存在票价差退款:,order_sn-{$order_info->getOrderSn()}";
        $this->refundInfos          = [];
        $this->refund_datas         = [];
        $this->has_alone_pay_record = false;
        $this->dealRefundData();
    }

    /**
     * 按照票价相关的资明细处理
     * @return void
     */
    private function dealRefundData(): void
    {
        $order              = $this->order;
        $passengers         = $order->getPassengers();
        $refundDetailModel  = new TripOrderPassengerRefundDetail();
        $refund_detail_type = RefundDetail::TICKET_PRICE_DIFF_FEE_REFUND;
        /**
         * @var string                 $pre_refund_sn 预生成退款编号
         * @var TrainOrderPassenger    $passenger 订单乘客
         * @var TripOrderPassengerCost $ticket_cost 乘客成本明细
         */

        // 按比例计算 保留2 位
        $scale                  = 2;
        $orderCostRepo          = app(TripOrderCostsRepositoryInterface::class);
        $orderPassengerCostRepo = app(TripOrderPassengerCostsRepositoryInterface::class);
        $order_ticket_costs     = $orderCostRepo->listByOrder($order, ['parent_type' => TripOrderCost::COST_TYPE_TICKET]);
        $refunds                = [];
        foreach ($order_ticket_costs as $order_ticket_cost) {
            $related_pay_sn = $order_ticket_cost->getPaySn();
            if (!isset($refunds[$related_pay_sn])) {
                // 同一个支付单用同一个 退款单号
                $refunds[$related_pay_sn]['refund_sn']     = generate_order_id(TripTypes::TRAIN, OrderBusiness::REFUND); // 初始化退款单号
                $refunds[$related_pay_sn]['refund_amount'] = 0;                                                          // 初始化退款金额
                $refunds[$related_pay_sn]['details']       = [];                                                         // 初始化退费明细
            }
            $pre_refund_sn                   = $refunds[$related_pay_sn]['refund_sn'];
            $details[$related_pay_sn]        = [];// 初始化
            $should_refunds[$related_pay_sn] = [];// 初始化
            foreach ($passengers as $passenger) {
                // 如果无票价差 跳过
                $ticket_price_diff = $passenger->getTicketDiff();
                $sub_passenger_id  = $passenger->subPassengerId();
                if (!($ticket_price_diff > 0)) {
                    Log::info("{$this->log_key},乘客id:{$sub_passenger_id}无票价差,订购价格:{$passenger->getSeatPrice()},出票价格:{$passenger->getTicketPrice()}");
                    continue;
                }

                $refund_rate = $passenger->getTicketDiffRefundRate();
                $ticket_cost = $orderPassengerCostRepo->findOneOrderPassengerTicketCost($passenger, $pre_refund_sn);
                $log_str     = "{$this->log_key},乘客id:{$sub_passenger_id}存在票价差:{$ticket_price_diff},应退比例为:{$refund_rate},支付单号:{$pre_refund_sn}";
                Log::info($log_str);
                // 通过乘客 + 退款类型 + 支付单号查询 退款记录
                $cost_refund_detail   = $refundDetailModel->getPassengerTicketPriceDiffRefund($passenger, $related_pay_sn);
                $cost_price           = $ticket_cost->getPrice();
                $should_refund_amount = bcmul($refund_rate, $cost_price, $scale);
                // 此时 剩余应退 -=应退
                if ($cost_refund_detail) {
                    $cost_refund_info = $cost_refund_detail->refund;
                    if (!$cost_refund_info->isRefundedSuccess()) {
                        $this->refundInfos[$cost_refund_info->getRefundSn()] = $cost_refund_info;
                    }
                    Log::info("{$log_str},存在退款明细记录,退款单号:{$cost_refund_info->getRefundSn()}");
                    continue;
                }
                Log::info("{$log_str},{}");
                // 一个支付单号一个乘客只记录一次
                $details[$related_pay_sn][$sub_passenger_id]        = [
                    "user_id"      => $passenger->getUserId(),
                    'passenger_id' => $passenger->getPassengerId(),
                    'sequence'     => $passenger->getSequence(),
                    'type'         => $refund_detail_type,
                    'refund_sn'    => $pre_refund_sn,
                    'order_sn'     => $order->getOrderSn(),
                    'out_trade_no' => $ticket_cost->getPaySn(),
                    'amount'       => $should_refund_amount,
                    'name'         => RefundDetail::getTypeDesc($refund_detail_type),
                ];
                $should_refunds[$related_pay_sn][$sub_passenger_id] = $should_refund_amount;
            }
            // 如果没有退款明细跳过
            if (empty($details[$related_pay_sn])) {
                unset($refunds[$related_pay_sn]);
                continue;
            }
            $refunds[$related_pay_sn]['refund_amount'] = array_sum($should_refunds[$related_pay_sn]);
            $refunds[$related_pay_sn]['details']       = array_values($details[$related_pay_sn]);
        }
        if (count($refunds) > 0) {
            $this->has_alone_pay_record = true;
        }
        $this->refund_datas = $refunds;
    }

    #[ArrayShape([
        TripOrderRefund::class,
    ])] public function getNeedRefundInfos(): array
    {
        return $this->refundInfos;
    }

    public function getNewRefundData(): array
    {
        return $this->refund_datas;
    }

    public function hasAlonePayRecord(): bool
    {
        return $this->has_alone_pay_record;
    }
}